
package at.fhj.ase.clientarch.server.db.dao.impl;

import java.io.Serializable;
import java.lang.reflect.ParameterizedType;
import java.util.List;

import javax.persistence.EntityManager;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import at.fhj.ase.clientarch.server.db.dao.GenericDAO;
import at.fhj.ase.clientarch.server.util.JPAUtil;

/**
 * @author cja, sfe | www.fh-joanneum.at | Client Architectures and Design
 */
public abstract class GenericDAOImpl<T, ID extends Serializable> implements GenericDAO<T, ID> {
  private Class<T> persistentClass;
  private EntityManager em;
  private int maxResults;
  private final Logger logger = LoggerFactory.getLogger(getClass().getName());

  @SuppressWarnings("unchecked")
  GenericDAOImpl() {
    this.persistentClass = (Class<T>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
    maxResults = 500; // TODO not really used yet => equals 8,3 minutes if there is stored a value each second
  }

  public void setEntityManager(EntityManager em) {
    this.em = em;
  }

  protected EntityManager getEntityManager() {
    if (em == null) {
      em = JPAUtil.getEmFactory().createEntityManager();
    }
    return em;
  }

  public Class<T> getPersistentClass() {
    return persistentClass;
  }

  public T findById(ID id) {
    return (T) getEntityManager().find(getPersistentClass(), id);
  }

  @SuppressWarnings("unchecked")
  public List<T> findAll() {
    return getEntityManager().createQuery("FROM " + getPersistentClass().getName()).setMaxResults(maxResults).getResultList();
  }

  public void insert(T entity) {
    begin();
    getEntityManager().persist(entity);
    commit();
  }

  public void update(T entity) {
    begin();
    getEntityManager().merge(entity);
    commit();
  }

  public void delete(T entity) {
    begin();
    getEntityManager().remove(getEntityManager().merge(entity));
    commit();
  }

  protected void begin() {
    getEntityManager().getTransaction().begin();
  }

  protected void commit() {
    try {
      getEntityManager().getTransaction().commit();
    } catch (Throwable t) {
      logger.error("Could not commit transaction, performling rollback...", t);
      rollback();
    }
  }

  @Override
  public void rollback() {
    if (getEntityManager().getTransaction().isActive()) {
      try {
        getEntityManager().getTransaction().rollback();
      } catch (Throwable t) {
        logger.error("Could not rollback transaction due to following error:", t);
      }
    }
  }

  public final int getMaxResults() {
    return maxResults;
  }

  public final void setMaxResults(int maxResults) {
    this.maxResults = maxResults;
  }
}
